package com.ruoyi.learn.java.design.pattern.struct;

/**
 * 享元模式 (Flyweight Pattern) 示例：优化图形编辑器中的图标绘制。
 */
public class FlyweightPatternDemo {

    //region ========== 1. 享元 (Flyweight) ==========

    /**
     * 享元接口：图标
     * 定义了享元对象的公共接口。
     * draw 方法接收外部状态（位置）。
     */
    public interface Icon {
        /**
         * 根据外部状态（位置）绘制图标。
         * @param x 图标在画布上的 X 坐标（外部状态）
         * @param y 图标在画布上的 Y 坐标（外部状态）
         */
        void draw(int x, int y);
    }

    //endregion

    //region ========== 2. 具体享元 (ConcreteFlyweight) ==========

    /**
     * 具体享元：具体图标
     * 存储内部状态（类型、颜色），这些状态是不可变的且可以被共享。
     * 实现了 Icon 接口。
     */
    public static class ConcreteIcon implements Icon {
        // 内部状态 (Intrinsic State) - 由享元工厂在创建时设定，不可变，可共享
        private final String type;  // 图标类型，如 "save", "print"
        private final String color; // 图标颜色，如 "blue", "red"

        // 构造函数：只接收内部状态
        public ConcreteIcon(String type, String color) {
            this.type = type;
            this.color = color;
            System.out.println("      > 【创建】新享元对象: " + type + "-" + color);
        }

        @Override
        public void draw(int x, int y) {
            // 使用内部状态和外部状态进行绘制
            System.out.println("        > [绘制] " + color + " " + type + " 图标 @ (" + x + ", " + y + ")");
        }

        // 可以提供 getter 方法，但通常内部状态是私有的
        public String getType() { return type; }
        public String getColor() { return color; }
    }

    //endregion

    //region ========== 3. 享元工厂 (FlyweightFactory) ==========

    /**
     * 享元工厂：图标工厂
     * 负责创建和管理 ConcreteIcon 享元对象。
     * 使用 Map 缓存已创建的享元，确保唯一性。
     */
    public static class IconFactory {
        // 使用 Map 作为缓存池，键是 type-color 的组合，值是享元对象
        private static final java.util.Map<String, Icon> iconPool = new java.util.HashMap<>();

        /**
         * 获取享元对象的静态工厂方法。
         * 这是客户端获取享元的主要方式。
         * @param type 图标类型
         * @param color 图标颜色
         * @return 共享的 ConcreteIcon 对象
         */
        public static Icon getIcon(String type, String color) {
            String key = type + "-" + color;

            // 检查缓存中是否已存在该享元
            if (!iconPool.containsKey(key)) {
                // 如果不存在，则创建新的享元对象并放入缓存
                System.out.println("    > 【工厂】缓存中无 '" + key + "'，正在创建...");
                iconPool.put(key, new ConcreteIcon(type, color));
            } else {
                System.out.println("    > 【工厂】从缓存中获取 '" + key + "'");
            }

            return iconPool.get(key);
        }

        /**
         * 获取缓存中享元对象的数量（用于演示）
         */
        public static int getPoolSize() {
            return iconPool.size();
        }

        /**
         * 清理缓存（可选，在某些场景下可能需要）
         */
        public static void clearPool() {
            iconPool.clear();
        }
    }

    //endregion

    //region ========== 4. 客户端 (Client) - 图形编辑器 ==========

    /**
     * 图形编辑器客户端
     * 使用享元模式来绘制大量图标。
     * 它不直接创建 ConcreteIcon，而是通过 IconFactory 获取。
     */
    public static class GraphicsEditor {
        // 存储所有要绘制的图标及其位置（外部状态）
        private java.util.List<Icon> icons = new java.util.ArrayList<>();
        private java.util.List<Integer> xCoords = new java.util.ArrayList<>();
        private java.util.List<Integer> yCoords = new java.util.ArrayList<>();

        /**
         * 添加一个图标到编辑器。
         * @param type 图标类型
         * @param color 图标颜色
         * @param x 图标 X 坐标（外部状态）
         * @param y 图标 Y 坐标（外部状态）
         */
        public void addIcon(String type, String color, int x, int y) {
            // 关键：通过工厂获取享元，而不是 new ConcreteIcon(...)
            Icon icon = IconFactory.getIcon(type, color);
            icons.add(icon);
            xCoords.add(x);
            yCoords.add(y);
            System.out.println("    > 【编辑器】添加图标 '" + type + "' (" + color + ") @ (" + x + ", " + y + ")");
        }

        /**
         * 绘制所有图标。
         * 遍历所有图标，调用其 draw 方法，并传入外部状态（位置）。
         */
        public void drawAll() {
            System.out.println("\n【客户端】开始绘制所有图标...");
            for (int i = 0; i < icons.size(); i++) {
                Icon icon = icons.get(i);
                int x = xCoords.get(i);
                int y = yCoords.get(i);
                icon.draw(x, y); // 将外部状态传递给享元
            }
            System.out.println("【客户端】所有图标绘制完成。\n");
        }

        public int getIconCount() {
            return icons.size();
        }
    }

    //endregion

    //region ========== 5. 主函数 (Main) - 演示享元模式 ==========

    public static void main(String[] args) {
        System.out.println("=== 享元模式演示 ===\n");

        // 1. 创建图形编辑器
        System.out.println("1. 创建图形编辑器...");
        GraphicsEditor editor = new GraphicsEditor();

        // 2. 添加大量图标（模拟真实场景）
        System.out.println("2. 向编辑器添加大量图标...\n");

        // 我们将添加 8 个图标，但只有 4 种独特的 (type, color) 组合
        String[][] iconData = {
                {"save", "blue", "10", "20"},
                {"save", "blue", "50", "60"}, // 与第一个相同类型和颜色，应复用
                {"save", "red", "90", "100"},
                {"print", "blue", "130", "140"},
                {"print", "red", "170", "180"},
                {"save", "blue", "210", "220"}, // 再次复用 save-blue
                {"print", "blue", "250", "260"}, // 再次复用 print-blue
                {"save", "red", "290", "300"}  // 再次复用 save-red
        };

        for (String[] data : iconData) {
            String type = data[0];
            String color = data[1];
            int x = Integer.parseInt(data[2]);
            int y = Integer.parseInt(data[3]);
            editor.addIcon(type, color, x, y);
        }

        System.out.println("\n当前缓存池大小: " + IconFactory.getPoolSize() + " (应为 4)");
        System.out.println("编辑器中图标总数: " + editor.getIconCount() + " (应为 8)");

        // 3. 执行绘制操作
        System.out.println("\n3. 执行绘制操作...");
        editor.drawAll();

        // 4. 演示享元的优势
        System.out.println("--- 享元模式的优势 ---");
        System.out.println("• 如果不使用享元模式，需要创建 " + editor.getIconCount() + " 个图标对象。");
        System.out.println("• 使用享元模式后，只创建了 " + IconFactory.getPoolSize() + " 个享元对象进行复用。");
        System.out.println("• 内存节省显著，尤其当对象数量巨大时。");

        // 5. 注意事项
        System.out.println("\n--- 注意事项 ---");
        System.out.println("• 内部状态必须是不可变的，以确保共享的安全性。");
        System.out.println("• 外部状态必须由客户端正确管理和传递。");
        System.out.println("• 享元工厂通常设计为单例或静态类，确保全局唯一缓存。");
        System.out.println("• 过度使用享元可能导致代码复杂化，仅在对象数量巨大且状态可分离时使用。");

        // 6. 性能对比（概念）
        System.out.println("\n--- 性能对比（概念） ---");
        System.out.println("假设每个 ConcreteIcon 对象占用 100 字节：");
        System.out.println("  - 无享元: " + editor.getIconCount() + " objects * 100B = " + (editor.getIconCount() * 100) + " B");
        System.out.println("  - 有享元: " + IconFactory.getPoolSize() + " flyweights * 100B + " + editor.getIconCount() + " sets of extrinsic state");
        System.out.println("    (外部状态通常很小，如两个 int，约 8-16B，总开销远小于 800B)");
        System.out.println("内存节省比例巨大！");

        // 7. 清理（可选）
        // IconFactory.clearPool(); // 释放缓存
    }

    //endregion
}
